package com.tinem.platform.module.starter.web.exception;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tinem.platform.module.pojo.co.GatewayHeadName;
import com.tinem.platform.module.pojo.vo.error.PlatformException;
import com.tinem.platform.module.starter.sdk.MessageCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.support.WebExchangeBindException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

@Slf4j
@ControllerAdvice
public class JsonExceptionHandler implements ResponseBodyAdvice {

    @Resource
    MessageSource messageSource;

    @ResponseBody
    @ExceptionHandler(Throwable.class)
    public void handle(HttpServletRequest request, HttpServletResponse response, Throwable throwable) {
        log.error("error",throwable);

        response.setStatus(HttpStatus.OK.value());
        response.setHeader(GatewayHeadName.X_PLATFORM_RES_SUCCESS,"false");
        response.setHeader(GatewayHeadName.X_PLATFORM_RES_TIMESTAMP,System.currentTimeMillis()+"");
        if(throwable instanceof MethodArgumentNotValidException){
            List<FieldError>  fieldErrorList = ((MethodArgumentNotValidException) throwable).getBindingResult().getFieldErrors();
            String message = JSON.toJSONString(fieldErrorList.stream().map(e -> new JSONObject(){{put(e.getField(),e.getDefaultMessage());}}).collect(Collectors.toList()));
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_CODE,MessageCode.ERROR_COMMONS_PARAMS_FIELD.name());
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_MESSAGE, Base64.encode(MessageCode.ERROR_COMMONS_PARAMS_FIELD.getMessage(getLocale(request),message)));
        }else if(throwable instanceof WebExchangeBindException){
            WebExchangeBindException exception = (WebExchangeBindException) throwable;
            String message = JSON.toJSONString(exception.getAllErrors().stream().map(e->(FieldError)e).map(e -> new JSONObject(){{put(e.getField(),e.getDefaultMessage());}}).collect(Collectors.toList()));
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_CODE,MessageCode.ERROR_COMMONS_PARAMS_FIELD.name());
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_MESSAGE, Base64.encode(MessageCode.ERROR_COMMONS_PARAMS_FIELD.getMessage(getLocale(request),message)));
        }else  if(throwable instanceof PlatformException){
            MessageCode code = ((PlatformException) throwable).getCode();
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_CODE,code.name());
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_MESSAGE,Base64.encode(code.getMessage(getLocale(request),((PlatformException) throwable).getArgs())));
        }else{
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_CODE,MessageCode.ERROR_COMMONS_UNKNOWN.name());
            String message = getMessage(request,throwable,MessageCode.ERROR_COMMONS_UNKNOWN);
            response.setHeader(GatewayHeadName.X_PLATFORM_RES_MESSAGE, Base64.encode(message.getBytes(StandardCharsets.UTF_8)));
        }
    }
    private Locale getLocale(HttpServletRequest request){
        String l = request.getHeader(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_LANG);
        Locale lang = Locale.getDefault();
        if(StrUtil.isNotEmpty(l)){
            l = l.replaceAll("_","-");
            lang =  Locale.forLanguageTag(l);
        }
        return lang;
    }

    private String getMessage(HttpServletRequest request, Throwable throwable,MessageCode messageCode){
        return messageCode.getMessage(getLocale(request),StrUtil.format("{}:{}",throwable.getClass().getName(),throwable.getMessage()));
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        serverHttpResponse.getHeaders().set(GatewayHeadName.X_PLATFORM_RES_SUCCESS,"true");
        serverHttpResponse.getHeaders().set(GatewayHeadName.X_PLATFORM_RES_CODE,MessageCode.SUCCESS.name());
        serverHttpResponse.getHeaders().set(GatewayHeadName.X_PLATFORM_RES_MESSAGE,MessageCode.SUCCESS.getMessage());
        serverHttpResponse.getHeaders().set(GatewayHeadName.X_PLATFORM_RES_TIMESTAMP,System.currentTimeMillis()+"");
        return body;
    }
}